/*********************************************************
          File Name:config.c
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Sat 01 Aug 2015 09:17:03 PM CST
**********************************************************/

#include "cfg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void node_delete(list_t *node)
{
        free(node->key);
        free(node->val);
        free(node);

        node = NULL;
}

list_t *list_new(void)
{
        list_t *list = NULL;
        list = (list_t *)malloc(sizeof(list_t));
        if(list == NULL)
                return NULL;
        list->next = NULL;

        return list;
}

int list_add(list_t *lt, char *key, char *val)
{
        size_t key_len, val_len;
        list_t *tmp = list_new();
        if(tmp == NULL)
                return -1;
        key_len = strlen(key) + 1;
        val_len = strlen(val) + 1;

        tmp->key = (char *)malloc(key_len);
        if(tmp->key == NULL)
                return -1;
        tmp->val = (char *)malloc(val_len);
        if(tmp->val == NULL)
        {
                free(tmp->key);
                return -1;
        }
        memset(tmp->key, 0, key_len);
        memset(tmp->val, 0, val_len);

        memcpy(tmp->key, key, key_len);
        memcpy(tmp->val, val, val_len);

        tmp->next = lt->next;
        lt->next = tmp;

        return 0;
}

void list_delete(list_t *head)
{
        list_t *cur, *tmp;
        cur = head->next;
        while(cur != NULL)
        {
                tmp = cur;
                cur = cur->next;
                node_delete(tmp);
        }
        free(head);
}


char *list_getval(list_t *head, char *key)
{
        size_t key_len = strlen(key) + 1;
        list_t *tmp = head->next;

        for(;tmp != NULL;)
        {
                if(memcmp(tmp->key, key, key_len) == 0)
                        return tmp->val;
                tmp = tmp->next;
        }
        return NULL;
}

static char *get_file_buf(const char *path)
{
        FILE *fp = NULL;
        char *buf = NULL;
        long size;

        fp = fopen(path, "r");
        if(fp == NULL)
                return NULL;
        fseek(fp, 0L, SEEK_END);
        size = ftell(fp);
        rewind(fp);

        buf = (char *)malloc(size);
        if(buf == NULL)
        {
                fclose(fp);
                return NULL;
        }

        fread(buf, size, 1, fp);
        fclose(fp);

        buf[size -1] = '\0';
        return buf;
}

static line_t get_line_buf(char *buf, char **line, size_t *offset)
{
        char *tmp = buf + *offset;
        long i = 0;

        switch(*tmp)
        {
                case '\0':
                    return LINE_EOL;
                case '\n':
                case '\r':
                    *offset += 1;
                    return LINE_CMT;
                case '#':
                    while(*tmp != '\n' && *tmp != '\0')
                    {
                        *offset += 1;
                        tmp += 1;
                    }
                    return LINE_CMT;
        }

        for(; tmp[i] != '\n'; ++i)
                *offset += 1;
        i++;                            // including '\n'
        *line = (char *)malloc(i);
        if(*line == NULL)
                return LINE_BAD;
        memset(*line, 0, i);

        memcpy(*line, tmp, i);
        (*line)[i-1] = '\0';
        
        return LINE_OK;
}

static int parse_line(char *line, list_t *lt)
{
        int i = 0;
        char *hold = line;
        char *key = NULL, *value = NULL;

        while(line[i] != '=')
                i++;
        if((key = (char *)malloc(i)) == NULL)
                return -1;
        memset(key, 0, i);
        memcpy(key, line, i);
        key[i-1] = '\0';

        line += i + 2;          // seek to key starter

        i = 0;
        while(line[i] != '\n' && line[i] != '#' && line[i] != '\0')
                i++;
        i += 1;
        if((value = (char *)malloc(i)) == NULL)
        {
                free(key);
                return -1;
        }
        memset(value, 0, i);
        memcpy(value, line, i);
        value[i-1] = '\0';

        if(list_add(lt, key, value) == -1)
        {
                free(key);
                free(value);
                free(hold);
                return -1;
        }

        free(hold);
        free(key);
        free(value);

        return 0;
}

int file2list(const char *path, list_t *lt, size_t *offset)
{
        char *filebuf = NULL;
        char *line = NULL;
        line_t res;
        int breakall = 0;

        filebuf = get_file_buf(path);
        if(filebuf == NULL)
                return -1;

        for(;breakall == 0;)
        {
                res = get_line_buf(filebuf, &line, offset);
                switch(res)
                {
                        case LINE_EOL:
                                breakall = 1;
                                break;
                        case LINE_BAD:
                                return -1;
                        case LINE_CMT:
                                break;
                        case LINE_OK:
                                if(parse_line(line, lt) == -1)
                                        return -1;
                                break;
                }
        }
        free(filebuf);
        return 0;
}

int read_conf(conf_t *cfg, size_t *offset)
{
        char *process = NULL, *thread = NULL;
        char *port = NULL, *host = NULL, *backlog = NULL;
        char *pid_file = NULL;
        list_t *lt = list_new();
        if(lt == NULL)
                return -1;
        if(file2list(CONF_PATH, lt, offset) == -1)
        {
                list_delete(lt);
                return -1;
        }

        memset(cfg, 0, sizeof(conf_t));
        if((process = list_getval(lt, "process")) == NULL)
        {
                list_delete(lt);
                return -1;
        }
        cfg->process = atoi(process);
        if((thread = list_getval(lt, "thread")) == NULL)
        {
                list_delete(lt);
                return -1;
        }
        cfg->thread = atoi(thread);
        if((host = list_getval(lt, "host")) == NULL)
        {
                list_delete(lt);
                return -1;
        }
        memcpy(cfg->host, host, strlen(host));
        if((port = list_getval(lt, "port")) == NULL)
        {
                list_delete(lt);
                return -1;
        }
        memcpy(cfg->port, port, strlen(port));
        if((backlog = list_getval(lt, "backlog")) == NULL)
        {
                list_delete(lt);
                return -1;
        }
        cfg->backlog = atoi(backlog);

        if((pid_file = list_getval(lt, "pidfile")) == NULL)
        {
                list_delete(lt);
                return -1;
        }
        memcpy(cfg->pidfile, pid_file, strlen(pid_file));

        list_delete(lt);

        return 0;
}
